package frontend;

import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.io.*;
import daytrader.common.*;

/* ====================================================================
 * Tree Node of the Syntax Tree. A quick glance:
 * SyntaxTreeNode {
 *         type,                // The type of the node, enum type
 *         parent,              // The parenet of node 
 *         children,            // A list of children
 *         value                // A object type to store the value
 * }        
 * ====================================================================
 */

public class SyntaxTreeNode {

	public enum Type{
		NONE, 
		NUM, 
		INT,
		STRING, 
		LIST, 
		SELECT, 
		FUNC, 
		INDICATOR,
		BUY, 
		SELL, 
		CROSSOP, 
		OP, 
		LOGICAL, 
		PROG, 
		PRINT, 
		BUYSTRAT, 
		SELLSTRAT,
		LITSTRING,
		ID,
		PREDEFINED,
		
		/* Types in customer-defined functions */
		USERDEFINE,
		USERFUNC,
		SIGNATURE,
		TYPE,
		ARGS,
		ARGUMENT,
		BLOCK,
		FUNCSTAT,
		IFGROUP,
		IF, 
		ELSEIF,
		ELSE,
		FOR,
		FORCOND,
		WHILE,
		ASSIGN,
		TF,
		RETURN,
		BREAK,
		CONTINUE,
	}
	
	//private NodeType type; 
	private Type type = null;
	public SyntaxTreeNode parent = null;
	private ArrayList<SyntaxTreeNode> children = null;
	private Object value;   /* save the parsed token */
	private int lineNumber = 0;
	private String valueType = "";
	private Boolean firstAssign = false;
	
	public SyntaxTreeNode (Object obj, Type nodetype, int line) {
		this.type = nodetype;
		this.parent = null;
		this.value = obj;
		this.children = new ArrayList<SyntaxTreeNode>();
        lineNumber = line;
	}
	public void SetType(Type nodetype)
	{
		type = nodetype;
	}
	
	public Type getType(){
		return type;
	}
	
	public void setParent(SyntaxTreeNode parentnode)
	{
		this.parent = parentnode;
	}
	
	public void addChild(SyntaxTreeNode child) {
		child.setParent(this);
		children.add(child);
	}	
	
	public void addChildAtHead(SyntaxTreeNode child) {
		child.setParent(this);
		children.add(0, child);
	}
	
	public ArrayList<SyntaxTreeNode> getChildren() {
		return children;
	}
	
	public SyntaxTreeNode getChild(int index) {
		return children.get(index);
	}
	
	public int getChildrenCount() {
		return children.size();
	}
	
	public SyntaxTreeNode getParent() {
		return parent;
	}
	
	public Object getValue() {
		return value;
	}	
	
	public String getStringValue() {
		return value.toString();
	}
	
	public void setValue(Object object) {
		value = object;
	}
	
	public String getValueType() {
		return valueType;
	}
	
	public void setValueType(String vt) {
		valueType = vt;
	}

	public int getLineNumber() {
		return this.lineNumber;
	}

	public void setLineNumber(int lineno) {
		this.lineNumber = lineno;
	}
	
	public Boolean getFirstAssign() {
		return this.firstAssign;
	}
	
	public void setFirstAssign(Boolean first) {
		this.firstAssign = first;
	}
	
	public void print(int level, int upperbound, Boolean verbose) {
		String prefix = "";
		if (level > upperbound) return;
		for (int i = 0; i < level; i++)
			prefix += "    ";

		if (this.getChildrenCount() > 0) {
			if (this.type == SyntaxTreeNode.Type.OP ) {
				if (verbose) {
					System.out.println(prefix + "line:" + this.lineNumber+"  "+this.value + " => [");
				}
				else {
					System.out.println(prefix + this.value + " => [");
				}
			}
			else 
			{
				if (verbose) {
					System.out.print(prefix + "line:" + this.lineNumber+"  "+ this.type);
				}
				else {
					System.out.print(prefix + this.type);
				}
				
				if (this.value != null) System.out.print(":" + this.value);
				if (verbose) System.out.print(",FirstAssign:"+this.getFirstAssign());
				System.out.println(",");
			}
			
			for (SyntaxTreeNode child : this.children) {
				child.print(level + 1, upperbound, verbose);
			}
			System.out.println(prefix + "],");
		} else {
			if (verbose)
				System.out.print(prefix + "line:" + this.lineNumber+"  "+  this.type);
			else 
				System.out.print(prefix + this.type);
			if (this.value != null) System.out.print(":" + this.value);
			if (verbose) {
				System.out.print(",FirstAssign:"+this.getFirstAssign());
				System.out.print(",ValueType:"+this.getValueType());
			}
			System.out.println(",");
		}
		
	}
	
	public SyntaxTreeNode getFirstChild(){
		return this.getChildren().get(0);
	}
	
	public SyntaxTreeNode getLastChild() {
		if (this.getChildrenCount() == 0) return null;
		return this.getChild(this.getChildrenCount() - 1);
	}
	
	public SyntaxTreeNode getNextSibling() {
		SyntaxTreeNode parent = this.parent;
		int idx = this.findIndexInParent();
		if (idx + 1 >= parent.children.size()) return null;
		else return parent.children.get(idx + 1);
	}
	
	public SyntaxTreeNode getPrevSibling() {
		SyntaxTreeNode parent = this.parent;
		int idx = this.findIndexInParent();
		if (idx - 1 < 0) return null;
		else return parent.children.get(idx - 1);
	}
	
	public void switchChild(int i, int j) {
		SyntaxTreeNode n1 = this.children.get(i);
		SyntaxTreeNode n2 = this.children.get(j);
		this.children.set(i, n2);
		this.children.set(j, n1);
	}
	
	private int findIndexInParent(){
		SyntaxTreeNode parent = this.parent;
		int i = 0;
		for (SyntaxTreeNode n : parent.children) {
			if (n == this) break;
			i++;
		}
		return i;
	}
	
	public String toString()
	{
	    // implementation for leaf nodes
	    if (value!=null)
	    	return value.toString();
	    else
		return "";
	}
	public boolean hasNextSibling() {
		SyntaxTreeNode parent = this.getParent();
		SyntaxTreeNode lastSibling = parent.getLastChild();
		if (this==lastSibling)
			return  false;
		
		return true;
	}
	
	public boolean isBranchNode(){
		return (this.getType()==SyntaxTreeNode.Type.IF
				||this.getType()==SyntaxTreeNode.Type.ELSE
				||this.getType()==SyntaxTreeNode.Type.ELSEIF);
	}
	public boolean isFuncRootNode(){
		return (this.getType() == SyntaxTreeNode.Type.USERFUNC);
	}
}
